#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sysmacros.h>

#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>

#define MAX_LINE 255

#define cyan(string) \
	do{ \
		char s[MAX_LINE]; \
		readlink(string, s, MAX_LINE); \
		printf("\033[1;36m%-4s -> %s\033[m\n", string, s); \
	}while(0)

#define blue(string) \
	do{printf("\033[1;32;34m%-4s\033[m\n", string);}while(0)

#define yellowBold(string) \
	do{printf("\033[1;33;40m%-4s\033[m\n", string);}while(0)

#define yellowLight(string) \
	do{printf("\033[0;33;40m%-4s\033[m\n", string);}while(0)

#define purple(string) \
	do{printf("\033[1;35m%-4s\033[m\n", string);}while(0)

#define other(string) \
	 do{printf("%-4s\n", string);}while(0)

void err_sys(const char *info)
{
	perror(info);
	exit(1);
}

void err_quit(const char *info)
{
	fprintf(stderr, "%s", info);
	exit(2);
}

char *mode(struct stat *info)
{
	static char mode[10];

	switch(info->st_mode & S_IFMT)
	{
		case S_IFSOCK:mode[0] = 's';break;
		case S_IFLNK: mode[0] = 'l';break;
		case S_IFDIR: mode[0] = 'd';break;
		case S_IFREG: mode[0] = '-';break;
		case S_IFBLK: mode[0] = 'b';break;
		case S_IFCHR: mode[0] = 'c';break;
		case S_IFIFO: mode[0] = 'p';
	}
	
	char rwx[] = {'r', 'w', 'x'};
	int i;
	for(i=0; i<10; i++)
		snprintf(mode+i+1, 10, "%c",
			(info->st_mode&(0400>>i))?rwx[i%3]:'-');

	return mode;
}

int hln(struct stat *info)
{
	return info->st_nlink;
}

char *user(struct stat *info)
{
	struct passwd *pw;
	pw = getpwuid(info->st_uid);
	if(pw == NULL)
	{
		static char uid[8];
		snprintf(uid, 8, "%u", info->st_uid);
		return uid;
	}

	return pw->pw_name;
}

char *group(struct stat *info)
{
	struct group *gr;
	gr = getgrgid(info->st_gid);
	if(gr == NULL)
	{
		static char gid[8];
		snprintf(gid, 8, "%u", info->st_gid);
		return gid;
	}

	return gr->gr_name;
}

char *size_dev(struct stat *info)
{
	static char size_or_dev[10];

	if(((info->st_mode&S_IFMT) == S_IFBLK) ||
		((info->st_mode&S_IFMT) == S_IFCHR))
		snprintf(size_or_dev, 10,
			"%d,%4d", major(info->st_rdev), minor(info->st_rdev));
	else
		snprintf(size_or_dev, 10, "%d", (int)info->st_size);

	return size_or_dev;
}

char *mtime(struct stat *info)
{
	char *t;
	t = ctime(&info->st_mtime);
	return t;
}

void print_color_name(struct stat *info, char *path)
{
	switch(info->st_mode & S_IFMT)
	{
	case S_IFDIR:
		blue(path);break;
	case S_IFLNK:
		cyan(path);break;
	case S_IFCHR:
	case S_IFBLK:
		yellowBold(path);break;
	case S_IFIFO:
		yellowLight(path);break;
	case S_IFSOCK:
		purple(path);break;
	default:
		other(path);
	}
}

void display(struct stat *info)
{
	printf("%s%3d %7s %7s%8s %.16s ",
		mode(info),
		hln(info),
		user(info),
		group(info),
		size_dev(info),
		mtime(info));
}

int main(int argc, char **argv)
{
	if(argc >= 3)
	{
		printf("参数过多\n");
		exit(0);
	};

	char *target = ((argc==2) ? argv[1] : ".");

	struct stat info;
	bzero(&info, sizeof(info));
	if(lstat(target, &info) == -1) // stat()
	{
		perror("lstat failed");
		exit(0);
	}

	if(S_ISDIR(info.st_mode))
	{
		DIR *dp = opendir(target);
		if(chdir(target) == -1)
		{
			perror("chdir() failed");
			exit(0);
		}

		struct dirent *ep;
		while((ep=readdir(dp)) != NULL)
		{
			if(ep->d_name[0] == '.')
				continue;

			bzero(&info, sizeof(info));
			if(lstat(ep->d_name, &info) == -1)
			{
				perror("lstat failed");
				exit(0);
			}

			display(&info);
			print_color_name(&info, ep->d_name);
		}

		closedir(dp);
	}

	else
	{
		display(&info);
		print_color_name(&info, target);
	}
		
	exit(0);
}